#pragma once
#include <utility>
#include <cstddef>
#include "treduce.hpp"
namespace utils {
  template <typename T, T N>
  using make_iseq = std::make_integer_sequence<T, N>;
  template <typename T, T... Is>
  using iseq = std::integer_sequence<T, Is...>;
  template <typename... Ts>
  struct type_list {
  };
  template <>
  struct type_list<> {
    template <typename T>
    static constexpr bool has = false;
  };

  template <typename T, typename... Ts>
  struct type_list<T, Ts...> {
    typedef T cur_type;
    typedef type_list<Ts...> next;
    template <typename U>
    static constexpr bool has = std::is_same<U, T>::value || next::template has<U>;
  };

  template <typename T, int I>
  struct get_type {
    typedef typename get_type<typename T::next, I - 1>::type type;
  };
  template <typename T>
  struct get_type<T, 0> {
    typedef typename T::cur_type type;
  };
  template <typename T, typename U, int I=0>
  struct get_ord {
    static constexpr int value = std::is_same<typename T::cur_type, U>::value ? I : get_ord<typename T::next, U, I+1>::value;
  };
  template<typename U, int I>
  struct get_ord<type_list<>, U, I> {
    static constexpr int value = -1;
  };
  template<typename ...Ts>
  struct variant {
    static constexpr size_t size = treduce::max(sizeof(Ts)...);
    static constexpr size_t align = treduce::max(alignof(Ts)...);
    typedef utils::type_list<Ts...> variants;
    alignas(align) unsigned char data[size];
    template<typename T>
    operator T&(){
      static_assert(variants::template has<T>, "target type mismatch variant definition.");
      return *(T*)(data);
    }
    template<typename T>
    T &as(){
      static_assert(variants::template has<T>, "target type mismatch variant definition.");
      return *(T*)(data);
    }
    template<int ITH>
    typename get_type<variants, ITH>::type &as(){
      return *(typename get_type<variants, ITH>::type*)(data);
    }
  };
  template<typename U, int I, typename ...Ts>
  struct get_ord<variant<Ts...>, U, I> {
    static constexpr int value = get_ord<type_list<Ts...>, U>::value;
  };
  using expand = int[];
} // namespace utils




